package common

import (
	"bufio"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	rand1 "math/rand"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"

	"fyne.io/systray"
	"gitee.com/freedom_feel/gui-bos-api/cmd/initialize/client"
	_type "gitee.com/freedom_feel/gui-bos-api/common/type"
	util "gitee.com/freedom_feel/gui-bos-api/common/utils"
	"gitee.com/freedom_feel/gui-bos-api/global"
	"gitee.com/freedom_feel/gui-bos-api/logs"
	"gitee.com/freedom_feel/gui-bos-api/models"
	"github.com/bytedance/sonic"
	"github.com/gin-gonic/gin"
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/launcher"
	"github.com/shopspring/decimal"
	"github.com/yinheli/qqwry"
	"gopkg.in/gomail.v2"
)

func InitDir() {
	// 1. 初始化目录（确保保存目录和临时分块目录存在）
	if err := os.MkdirAll(global.SaveDir, 0755); err != nil {
		log.Fatalf("创建保存目录失败: %v", err)
	}
	fmt.Println("创建文件成功")
	tempChunkDir := filepath.Join(os.TempDir(), "upload_chunks")
	if err := os.MkdirAll(tempChunkDir, 0755); err != nil {
		log.Fatalf("创建临时分块目录失败: %v", err)
	}
}

// 验证 cookie
func VerifyCookie(cookies []*http.Cookie) (uid string, verify bool) {
	var reqCookie string
	for _, cookie := range cookies {
		if cookie.Name != global.CookieKey {
			continue
		} else {
			reqCookie = cookie.Name + "=" + cookie.Value
		}
	}
	logs.Info("reqCookie：" + reqCookie)

	if reqCookie == "" {
		return "", false
	}
	if !strings.HasPrefix(reqCookie, global.CookieKey) || !strings.Contains(reqCookie, global.CookieKey) {
		return "", false
	}
	var TokenArray []string
	var token string
	if strings.Contains(reqCookie, "-") {
		TokenArray = strings.Split(reqCookie, "-")
		if len(TokenArray[1:len(TokenArray)-1]) > 1 {
			for i, v := range TokenArray[1 : len(TokenArray)-1] {
				if i == 0 {
					token += v
				} else {
					token += "-" + v
				}
			}
		} else {
			token = TokenArray[1 : len(TokenArray)-1][0]
		}
		logs.Info(fmt.Sprintf("reqToken：%v", token))
	}
	//最后解析 token
	uid, expiresAt := util.ParseToken(token)
	if uid == "" {
		logs.Info("空数据或恶意数据...返回")
		return "", false
	}
	_type.PrintfLog(logs.Logerr, "reqToken ExpiresAt:", strconv.FormatInt(expiresAt, 10))
	_type.PrintfLog(logs.Logerr, "reqUid:", uid)
	//最后在比对下
	tokenKey := uid + global.TokenKey
	if client.RedisExist(tokenKey) {
		Token, err := client.RedisGet(tokenKey)
		if err != nil {
			logs.Error("redis token nil")
			_type.ErrChan <- *_type.ErrSystem(_type.StrPinJie("系统内部错误:", err.Error()))
			return "", false
		}
		logs.Info("redisToken：" + Token)
		if token != Token {
			return "", false
		}
	} else {
		return "", false
	}
	return uid, true
}

type TypeNil struct {
	Value any
	Str   string
	Int   int
	Flt   float64
	Bt    byte
	Bl    bool
	Err   error // 用 Err 代替 Df，更符合 Go 习惯
}

// AnyType 安全解析 Value 并填充对应字段
func (a *TypeNil) AnyType() {
	if a.Value == nil {
		a.Err = errors.New("value is nil")
		return
	}

	switch v := a.Value.(type) {
	case string:
		a.Str = a.AnyToStr()
	case int:
		a.Int = a.AnyToInt()
	case float64:
		a.Flt = a.AnyToFlt()
	case byte:
		a.Bt = a.AnyToBt()
	case bool:
		a.Bl = a.AnyToBl()
	default:
		a.Err = fmt.Errorf("unsupported type: %T", v)
	}
}

func (a *TypeNil) AnyToStr() string {
	return a.Value.(string)
}

func (a *TypeNil) AnyToInt() int {
	return a.Value.(int)
}

func (a *TypeNil) AnyToFlt() float64 {
	return a.Value.(float64)
}

func (a *TypeNil) AnyToBt() byte {
	return a.Value.(byte)
}

func (a *TypeNil) AnyToBl() bool {
	return a.Value.(bool)
}

func ByUidGetUserInfo(uid string) (userInfo models.User, err error) {
	userdataKey := uid + global.UserDtaKey
	var res string
	if client.RedisExist(userdataKey) {
		res, err = client.RedisGet(userdataKey)
		if err != nil {
			return userInfo, err
		}
		sonic.Unmarshal([]byte(res), &userInfo)
	} else {
		userInfo, err = client.GetUserInfoByUid(uid)
		if err != nil {
			return userInfo, err
		}
	}
	return userInfo, nil
}

// 解析SQL文件
func ParseSQLToMap(sqlFileName string) (err error) {
	// 读取 SQL 文件内容
	file, err := os.Open(sqlFileName)
	if err != nil {
		fmt.Println("Error opening file:", err)
		return
	}
	defer file.Close()

	// 创建一个 map 来存储 city_name 和 city_code
	global.AreaMap = make(map[string]string)

	// 改进后的正则表达式
	//insertRegex := regexp.MustCompile(`INSERT INTO \`area\` VALUES\s*\(\s*'([^']*)'\s*,\s*'([^']*)'\s*(?:,\s*[^)]*)?\)`)

	// 逐行读取文件内容
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		// 过滤注释和空行
		if strings.HasPrefix(line, "--") || strings.TrimSpace(line) == "" {
			continue
		}
		if strings.Contains(line, "INSERT") {
			idxArr := strings.Split(line, " ")
			//只获取城市名称和代码
			cityName, SymErr := removeSymbols(idxArr[4])
			if SymErr != nil {
				return
			}
			cityCode, SymErr := removeSymbols(idxArr[5])
			if SymErr != nil {
				return
			}
			global.AreaMap[cityName] = cityCode
		}
	}
	if err = scanner.Err(); err != nil {
		fmt.Println("Error reading file:", err)
		return
	}
	logs.Info("SQL Parse Success")
	return
}

// 去除符号
func removeSymbols(text string) (res string, err error) {
	if strings.Contains(text, "(") {
		text = strings.ReplaceAll(text, "(", "")
	}
	numberStr := strings.ReplaceAll(text, "'", "")
	numberStr = strings.TrimSuffix(numberStr, ",")
	if numberStr == "" {
		fmt.Println("Empty string")
		return "", errors.New("Empty string")
	}
	return numberStr, nil
}

// string to int
func StrToInt(str string) int {
	intValue, err := strconv.Atoi(str)
	if err != nil {
		intValue = int(StrToFloat(str))
	}
	return intValue
}

// int to string
func IntToStr(str int) string {
	return strconv.Itoa(str)
}

// string to Float
func StrToFloat(str string) float64 {
	str = strings.TrimSpace(str)
	str = TrimQff(str)
	intValue, err := strconv.ParseFloat(str, 64)
	if err != nil {
		intValue = 0
	}
	d := decimal.NewFromFloat(intValue)
	//dmlog.LoggerS.Info(n.String())
	f, _ := d.Round(2).Float64()
	return f
}

const charset1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const charset2 = "0123456789"

// randomString 使用crypto/rand生成一个随机字符串
func RandomString(length int) string {
	charset := ""
	if length <= 11 {
		charset = charset2
	} else {
		charset = charset1
	}
	bytes := make([]byte, length)
	if _, err := rand.Read(bytes); err != nil {
		return ""
	}

	for i, b := range bytes {
		// 这里需要将b转换为byte类型
		bytes[i] = charset[b%byte(len(charset))]
	}

	return string(bytes)
}

func RandomUUIDStyleNickname() string {
	uuidPart := fmt.Sprintf("%04x", rand1.Intn(0x10000)) // 4位随机16进制
	return _type.StrPinJie("user-", uuidPart)
}

// string 去千分符
func TrimQff(str string) string {
	return strings.Replace(str, ",", "", -1)
}

func StartsWithZero(s string) bool {
	return strings.HasPrefix(s, "0")
}

// 拦截 请求
func Hijack(p *rod.Page, pattern string) {
	r := p.HijackRequests()
	ch := make(chan string)
	r.MustAdd("*"+pattern+"*", func(ctx *rod.Hijack) {
		//代理使用再需要
		//if lr.UseProxy {
		//	err = ctx.LoadResponse(c, true)
		//	if err != nil {
		//		return
		//	}
		//} else {
		ctx.MustLoadResponse()
		//}
		ch <- ctx.Response.Body()
		ctx.Response.Payload()
	})
}

// 信息管道 ch 等待时间 t （单位：second）
func getRes(ch chan string, t float64) (string, error) {
	d := time.Duration(t * float64(time.Second))
	select {
	case val := <-ch:
		return val, nil
	case <-time.After(d):
		return "", errors.New("timeout")
	}
}

// 统一 成功 格式 需要嵌套再去map 不需要 保留一层
func Success(c *gin.Context, msg string, data interface{}) {
	if msg == "" {
		msg = "success"
	}
	c.JSON(http.StatusOK, gin.H{
		"code":    1,
		"message": msg,
		"data":    data,
	})
}

// 统一 失败 格式 *****绝对不能嵌套！
func FailError(c *gin.Context, msg string) {
	c.JSON(http.StatusOK, gin.H{
		"code":    -1,
		"message": msg,
		"data":    nil,
	})
}

// 统一 坏请求
func BadRequest(c *gin.Context, err error) {
	c.JSON(http.StatusOK, gin.H{
		"code":    http.StatusBadRequest,
		"message": err.Error(),
		"data":    nil,
	})
}

// 统一 服务 失败
func FailErrCode(c *gin.Context, msg string, code int) {
	c.JSON(http.StatusOK, gin.H{
		"code":    code,
		"message": msg,
		"data":    nil,
	})
}

// 简单的创建浏览器
func NewBrowser() *rod.Browser {
	l := launcher.New().Headless(false)
	// 防检测
	l.Flags["disable-blink-features"] = []string{"AutomationControlled"}
	ws, _ := l.Launch()
	return rod.New().ControlURL(ws).MustConnect()
}

// truncateString 根据字符数截取字符串
func TruncateString(s string, maxRunes int) string {
	if utf8.RuneCountInString(s) <= maxRunes {
		return s
	}

	// 截取至最后一个完整的字符
	n := 0
	for i, r := range s {
		if n+utf8.RuneLen(r) > maxRunes {
			return s[:i]
		}
		n++
	}
	return s
}

// 获取访问IP地址
func GetUserIp(g *gin.Context) {
	//c := http.DefaultClient
	//get, err := c.Get("https://myip.ipip.net")
	//if err != nil {
	//	return
	//}
	//defer get.Body.Close()
	//b, _ := io.ReadAll(get.Body)
	OpenWriteFile(global.UserIP, "北京时间："+time.Now().Format(time.DateTime)+" 访问信息："+getIpInfo1(GetClientIP(g.Request))+"\n")
}

func GetClientIP(r *http.Request) string {
	// Cloudflare 优先
	if cfIP := r.Header.Get("CF-Connecting-IP"); cfIP != "" {
		return cfIP
	}

	// 处理 X-Forwarded-For
	if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
		ips := strings.Split(forwarded, ",")
		if ip := strings.TrimSpace(ips[0]); ip != "" {
			return ip
		}
	}

	// 处理 X-Real-IP
	if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
		return realIP
	}

	// 默认从 RemoteAddr 获取
	ip, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		return r.RemoteAddr
	}
	return ip
}

func getIpInfo1(ip string) string {
	// 自动下载纯真数据库（如果本地不存在）
	if _, err := os.Stat("qqwry.dat"); os.IsNotExist(err) {
		fmt.Println("正在下载纯真IP库...")
		resp, err := http.Get("https://qqwry.mirror.noc.one/qqwry.dat")
		if err != nil {
			panic("下载失败: " + err.Error())
		}
		defer resp.Body.Close()

		out, _ := os.Create("qqwry.dat")
		defer out.Close()

		_, err = io.Copy(out, resp.Body)
		if err != nil {
			panic("保存文件失败: " + err.Error())
		}
		fmt.Println("下载完成！")
	}

	// 使用纯真库查询
	q := qqwry.NewQQwry("qqwry.dat")
	q.Find(ip)

	fmt.Printf("IP %s\n", q.Ip)       // 示例输出：山东省青岛市
	fmt.Printf("位置: %s\n", q.Country) // 示例输出：山东省青岛市
	fmt.Printf("运营商: %s\n", q.City)   // 示例输出：联通
	return q.Ip + "-" + q.Country + "-" + q.City
}

func getIpInfo(ip string) string {
	url1 := fmt.Sprintf("https://www.cz88.net/api/cz88/ip/base?ip=%v", ip)
	method := "GET"

	client := &http.Client{}
	req, err := http.NewRequest(method, url1, nil)

	if err != nil {
		fmt.Println(err)
		return ""
	}
	req.Header.Add("DNT", "1")
	req.Header.Add("Pragma", "no-cache")
	req.Header.Add("Cookie", "Hm_lvt_5fe556ebe1c9d856695a9a35f4d18ce3=1754898265; Hm_lpvt_5fe556ebe1c9d856695a9a35f4d18ce3=1754898265; HMACCOUNT=BD007C8F76D30AFE; AGL_USER_ID=a7052664-2d01-4727-bf3c-d8cc4de8aa0f; _ga=GA1.1.1648226155.1754898265; _ga_SGEY66BFK5=GS2.1.s1754898265$o1$g0$t1754898265$j60$l0$h0; __bid_n=198981675ff132829802bd; sajssdk_2015_cross_new_user=1; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2219898167642c2c-0ff5d0f19a297-4c657b58-2073600-198981676432bdb%22%2C%22first_id%22%3A%22%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk4OTgxNjc2NDJjMmMtMGZmNWQwZjE5YTI5Ny00YzY1N2I1OC0yMDczNjAwLTE5ODk4MTY3NjQzMmJkYiJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%22%2C%22value%22%3A%22%22%7D%2C%22%24device_id%22%3A%2219898167642c2c-0ff5d0f19a297-4c657b58-2073600-198981676432bdb%22%7D")

	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	var ipdata IPres
	json.Unmarshal(body, &ipdata)
	return ipdata.Data.Ip + "-" + ipdata.Data.ActionAddress[0] + "-" + ipdata.Data.NetWorkType
}

type IPres struct {
	Code    int    `json:"code"`
	Success bool   `json:"success"`
	Message string `json:"message"`
	Data    struct {
		Ip            string   `json:"ip"`
		CountryCode   string   `json:"countryCode"`
		Country       string   `json:"country"`
		Province      string   `json:"province"`
		City          string   `json:"city"`
		Districts     string   `json:"districts"`
		Isp           string   `json:"isp"`
		Geocode       string   `json:"geocode"`
		NetWorkType   string   `json:"netWorkType"`
		MbRate        string   `json:"mbRate"`
		Score         string   `json:"score"`
		Honeypot      string   `json:"honeypot"`
		NetAddress    string   `json:"netAddress"`
		Asn           string   `json:"asn"`
		Iana          string   `json:"iana"`
		ActionAddress []string `json:"actionAddress"`
		Company       string   `json:"company"`
		Locations     []struct {
			Latitude  string  `json:"latitude"`
			Longitude string  `json:"longitude"`
			Radius    float64 `json:"radius"`
		} `json:"locations"`
		OpenPorts          []interface{} `json:"openPorts"`
		Domains            interface{}   `json:"domains"`
		BreadRateMap       interface{}   `json:"breadRateMap"`
		DeviceRateMap      interface{}   `json:"deviceRateMap"`
		ProvinceCode       string        `json:"provinceCode"`
		CityCode           string        `json:"cityCode"`
		DistrictCode       string        `json:"districtCode"`
		Continent          string        `json:"continent"`
		Vpn                bool          `json:"vpn"`
		Tor                bool          `json:"tor"`
		Proxy              bool          `json:"proxy"`
		Spider             bool          `json:"spider"`
		Roaming            bool          `json:"roaming"`
		IcloudPrivateRelay bool          `json:"icloudPrivateRelay"`
	} `json:"data"`
	Time string `json:"time"`
}

func OpenWriteFile(name, body string) {
	logs.Info(body)
	files, ferr := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if ferr != nil {
		logs.Error("打开文件失败:" + ferr.Error())
		return
	}
	defer files.Close()
	// 写入文件
	_, err := files.WriteString(body)
	if err != nil {
		logs.Error("写入文件时出错:" + err.Error())
		return
	}
	logs.Info("追加写入文件成功")
}

func onReady() {
	// 读取图标文件
	iconData, err := os.ReadFile("app.ico")
	if err != nil {
		log.Fatalf("Failed to read icon file: %v", err)
	}
	systray.SetIcon(iconData)             // 设置图标
	systray.SetTitle("gui-bos")           // 设置标题
	systray.SetTooltip("gui-bos 正在运行...") // 设置工具提示

	mQuit := systray.AddMenuItem("退出", "退出程序") // 添加退出菜单项
	//bbh := systray.AddMenuItem("版本："+global.Version, "版本号") // 添加退出菜单项
	go func() {
		for {
			select {
			case <-mQuit.ClickedCh: // 监听退出菜单项点击事件
				systray.Quit()
				//case <-bbh.ClickedCh:

			}
		}

	}()
}

func onExit() {
	// 清理资源
	fmt.Println("Received SIGINT. Exiting gracefully.")
	//initialize.CleanUpDB(global.Rdb)
}

// 邮件发送函数
func SendEmail(to []string, code, body string) error {
	// 邮件服务器配置
	var subject string
	if body == "" {
		subject = "Please verify your device!"
		body = fmt.Sprintf("Hi! \n\nA sign in attempt requires further verification because we did not recognize your device. To complete the sign in, enter the verification code on the unrecognized device.\n\nDevice: Chrome on Windows\nVerification code: %v\n\nIf you did not attempt to sign in to your account, your password may be compromised. Visit https://www.baidu.com/ to create a new, strong password for your GitHub account.\n\nIf you'd like to automatically verify devices in the future, consider enabling two-factor authentication on your account. Visit https://gitee.com/freedom_feel/gui-bos-api to learn about two-factor authentication.\n\nIf you decide to enable two-factor authentication, ensure you retain access to one or more account recovery methods. See https://gitee.com/freedom_feel/gui-bos-api in the GitHub Help.\n\nThanks,\nThe GitHub Team", code)
	} else {
		subject = "Warning!!! ErrSystem !!!"
	}

	// 创建一个新的邮件
	m := gomail.NewMessage()
	m.SetHeader("From", global.From) // 发件人 From = "wm224119@163.com"
	m.SetHeader("To", to...)         //收件人
	m.SetHeader("Subject", subject)  //邮件标题
	m.SetBody("text/plain", body)    //邮件内容

	// 创建 SMTP 客户端
	// 邮件服务器配置
	//SmtpServer   = "smtp.163.com"     // 邮件服务器地址
	//SmtpPort     = 587                // SMTP 端口（465 用于 SSL）
	//SmtpUser     = "wm224119@163.com" // SMTP 用户名
	//SmtpPassword = "WOIGZZHLZLLBAYWH" // SMTP 密码
	d := gomail.NewDialer(global.SmtpServer, global.SmtpPort, string(global.SmtpUserEmail), global.SmtpPassword)
	d.SSL = true // 确保启用 SSL/TLS 加密

	// 发送邮件
	if err := d.DialAndSend(m); err != nil {
		log.Fatal(err)
	}

	logs.Info("Email sent successfully!")
	return nil
}

// 路径 存在
func PathExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

// FileExtensionOptions 配置选项
type FileExtensionOptions struct {
	WithDot      bool // 是否包含点(.)
	ToLower      bool // 是否转为小写
	MultipleDots bool // 是否处理多个点的情况(如.tar.gz)
}

// GetFileExtension 获取文件后缀名
// filename: 文件名
// opts: 配置选项，nil表示使用默认选项
func GetFileExtension(filename string, opts *FileExtensionOptions) string {
	if opts == nil {
		opts = &FileExtensionOptions{
			WithDot:      true,
			ToLower:      false,
			MultipleDots: false,
		}
	}

	ext := filepath.Ext(filename)

	// 处理多个点的情况，如".tar.gz"
	if opts.MultipleDots {
		base := filepath.Base(filename)
		if i := strings.Index(base, "."); i > 0 {
			ext = base[i:]
		}
	}

	// 是否包含点
	if !opts.WithDot && len(ext) > 0 && ext[0] == '.' {
		ext = ext[1:]
	}

	// 是否转为小写
	if opts.ToLower {
		ext = strings.ToLower(ext)
	}

	return ext
}

func MapDelFiled(userMap map[string]interface{}, key []string) {
	for _, v := range key {
		delete(userMap, v)
	}
}
